#include "OiPCH.hpp"
#include "OptexSensor.hpp"
#include "OptexSensorData.hpp"
#include "Util/OiEthernet.hpp"

namespace Oi
{
    namespace PLScannerCore
    {
        using namespace PLScannerCore;

        class OptexSensorControlImpl : public Impl<OptexSensorControl>
        {
        public:
            void init();
            void exit();

        public:
            EthernetTask _task1;
            EthernetTask _task2;

            DataAnalysis _data;
        public:
            QString _lastFileName;

        public:
            QString _addr1;
            QString _port1;
            bool _enable1;

            QString _addr2;
            QString _port2;
            bool _enable2;

        public:
            bool _saveStatus;

        public:
            Base::Barrier _waiter1;
            Base::Barrier _waiter2;

        public:
            bool _abortDownload;

        public:
            double _value1;
            double _value2;
            double _temperature1;
            double _temperature2;

        public:
            int64 _timeTick1 = 0;
            int64 _timeTick2 = 0;
        };

        void OptexSensorControlImpl::init()
        {
            OI_Q(OptexSensorControl);

            _saveStatus = false;

            auto settings = Core::OiSettings();
            _enable1 = settings->value("Sensor1/Enable", false).toBool();
            _addr1 = settings->value("Sensor1/Address", "192.168.1.100").toString();
            _port1 = settings->value("Sensor1/Port", "9999").toString();

            _enable2 = settings->value("Sensor2/Enable", false).toBool();
            _addr2 = settings->value("Sensor2/Address", "192.168.1.101").toString();
            _port2 = settings->value("Sensor2/Port", "9999").toString();

            _task1.init(_enable1, "TCP", _addr1, _port1);
            _task2.init(_enable2, "TCP", _addr2, _port2);

            q->connect(&_task1, &EthernetTask::readReady, q, &OptexSensorControl::onReadReady1, Qt::DirectConnection);
            q->connect(&_task2, &EthernetTask::readReady, q, &OptexSensorControl::onReadReady2, Qt::DirectConnection);

        }

        void OptexSensorControlImpl::exit()
        {
            OI_Q(OptexSensorControl);
            q->abortDownload();
            _task1.exit();
            _task2.exit();
            Util::EthernetService::instance()->exit();
        }

        OptexSensorControl::OptexSensorControl()
        {
            OI_I(OptexSensorControl)->init();
        }

        OptexSensorControl::~OptexSensorControl()
        {
            OI_E(OptexSensorControl)->exit();
        }

        void OptexSensorControl::init()
        {
            OI_F(OptexSensorControl)->init();
        }

        void OptexSensorControl::exit()
        {
            OI_F(OptexSensorControl)->exit();
        }

        void OptexSensorControl::startCapture()
        {
            OI_F(OptexSensorControl);
            d->_waiter1.reset();

            char* pcmd = (char*)d->_data.WriteDataStorageFunctionControlCmd(1);

            char arry[10];
            memcpy(arry, pcmd, 10);

            d->_task1.write(QByteArray(arry, 10));

            if (PLScanner::instance()->debugging)
            {
                OiWarning() << "Start Capture command";
            }

            while (!d->_waiter1.wait(1000))
            {
                return;
            }

            d->_saveStatus = true;
        }

        void OptexSensorControl::stopCapture(int wait)
        {
            OI_F(OptexSensorControl);
            d->_waiter1.reset();

            char* pcmd = (char*)d->_data.WriteDataStorageFunctionControlCmd(0);

            char arry[10];
            memcpy(arry, pcmd, 10);

            d->_task1.write(QByteArray(arry, 10));

            if (PLScanner::instance()->debugging)
            {
                OiWarning() << "Stop Capture command";
            }

            while (!d->_waiter1.wait(1000))
            {
                return;
            }

            Sleep(wait);

            d->_saveStatus = false;
        }

        void OptexSensorControl::abortDownload()
        {
            OI_F(OptexSensorControl);

            d->_abortDownload = true;
            d->_data.abortDownload(true);
        }


        bool OptexSensorControl::getInfos(QMap<QDateTime, QString>& infos)
        {
            OI_F(OptexSensorControl);

            if (d->_abortDownload)
            {
                return false;
            }
            QString strFilePath = QString("http://%1//cgi-bin/sto_list.cgi").arg(d->_addr1);
            auto fileName = d->_data.GetFileNames(strFilePath.toStdString());

            infos.clear();

            for (auto iter = fileName.begin(); iter != fileName.end(); iter++)
            {
                QDateTime dt = QDateTime::fromTime_t(iter->first);
                QString fn = QString::fromStdString(iter->second);

                infos[dt] = fn;
            }

            return true;
        }

        bool OptexSensorControl::getProifile(std::vector<double>& profile, std::map<double, double>& tickProfile ,QVariantMap& infos, const int timeout /*= -1*/)
        {
            OI_F(OptexSensorControl);

            if (PLScanner::instance()->debugging)
            {
                d->_timeTick1 = QDateTime::currentDateTime().toMSecsSinceEpoch();
                OiWarning() << "Interval:" << d->_timeTick1 - d->_timeTick2;
                d->_timeTick2 = d->_timeTick1;
            }

            d->_abortDownload = false;
            d->_data.abortDownload(false);

            QString lastFileName;
            int loopTime = 0;

            QMap<QDateTime, QString> fileInfos;

            while(1)
            {
                getInfos(fileInfos);

                if (fileInfos.size() > 0)
                {
                    lastFileName = fileInfos.last();
                }

                if (lastFileName != d->_lastFileName)
                {
                    break;
                }

                loopTime++;

                if (d->_abortDownload)
                {
                    return false;
                }

                if (loopTime > 10)
                {
                    return false;
                }

                Sleep(100);
            } 

            if (PLScanner::instance()->debugging)
            {
                d->_timeTick1 = QDateTime::currentDateTime().toMSecsSinceEpoch();
                OiWarning() << "GetInfo:" << d->_timeTick1 - d->_timeTick2;
                d->_timeTick2 = d->_timeTick1;
            }
            
            d->_lastFileName = lastFileName;
            
            std::string fileUrl = "http://" + d->_addr1.toStdString() + "/storage/" + d->_lastFileName.toStdString();

            std::string ValueFilePath = PLScanner::instance()->dataDir.toStdString() + "/" + d->_lastFileName.toStdString();
            OiWarning() << "ValueFilePath:" << lastFileName << "   fileUrl:" << QString::fromStdString(ValueFilePath);
            if (d->_abortDownload)
            {
                return false;
            }

            if (!d->_data.DownloadFiles(fileUrl, ValueFilePath))
            {
                OiWarning() << "Download false:";
                return false;
            }

            if (PLScanner::instance()->debugging)
            {
                d->_timeTick1 = QDateTime::currentDateTime().toMSecsSinceEpoch();
                OiWarning() << "Download:" << d->_timeTick1 - d->_timeTick2;
                d->_timeTick2 = d->_timeTick1;
            }
            
            profile = d->_data.GetCSVFileValue(ValueFilePath, tickProfile);

            if (fileInfos.size() > 100)
            {
                char* pcmd = (char*)d->_data.deleteAllFileCmd();

                char arry[10];
                memcpy(arry, pcmd, 10);

                d->_task1.write(QByteArray(arry, 10));
            }

            if (PLScanner::instance()->debugging)
            {
                d->_timeTick1 = QDateTime::currentDateTime().toMSecsSinceEpoch();
                OiWarning() << "Decode:" << d->_timeTick1 - d->_timeTick2;
                d->_timeTick2 = d->_timeTick1;
            }
            
            return true;
        }

        bool OptexSensorControl::getValue(double& profile, const int timeout /*= -1*/)
        {
            OI_F(OptexSensorControl);

            d->_waiter1.reset();

            char* pcmd = (char*)d->_data.ReadCh1ValueCmd();

            char arry[4];
            memcpy(arry, pcmd, 4);

            if (PLScanner::instance()->debugging)
            {
                OiWarning() << "Start: getValue";
            }

            d->_task1.write(QByteArray(arry, 4));

            while (!d->_waiter1.wait(timeout))
            {
                if (PLScanner::instance()->debugging)
                {
                    OiWarning() << "Not get";
                }
                return false;
            }

            if (PLScanner::instance()->debugging)
            {
                OiWarning() << "Get Value:" << d->_value1;
            }

            profile = d->_value1 / 1e6;

            return true;
        }

        bool OptexSensorControl::loadFile(const QString file, std::vector<double>& profile, std::map<double, double>& tickProfile)
        {
            OI_F(OptexSensorControl);
           
            profile = d->_data.GetCSVFileValue(file.toStdString(), tickProfile);
            return true;
        }

        bool OptexSensorControl::getTemperature2(double& temperature)
        {
            OI_F(OptexSensorControl);

            d->_waiter2.reset();

            char* pcmd = (char*)d->_data.ReadSensorTemperature();

            char arry[4];
            memcpy(arry, pcmd, 4);

            d->_task2.write(QByteArray(arry, 4));

            while (!d->_waiter2.wait(2000))
            {
                return false;
            }
            temperature = d->_temperature2/ 1e3;

            return true;
        }

        bool OptexSensorControl::getTemperature1(double& temperature)
        {
            OI_F(OptexSensorControl);

            d->_waiter1.reset();

            char* pcmd = (char*)d->_data.ReadSensorTemperature();

            char arry[4];
            memcpy(arry, pcmd, 4);
            
            d->_task1.write(QByteArray(arry, 4));

            while (!d->_waiter1.wait(2000))
            {
                return false;
            }

            temperature = d->_temperature1 / 1e3;

            return true;
        }

        void OptexSensorControl::onReadReady1(const QByteArray data)
        {
            OI_F(OptexSensorControl);
          
            uchar* dd = (uchar*)data.data();

            if (data.size()>=2 && dd[0] == 0xC0 && dd[1] == 0x00)
            {
                if (PLScanner::instance()->debugging)
                {
                    OiWarning() << "Task1: Get Command return";
                }

                d->_waiter1.wake();
            }
            else if (data.size() >= 6 && dd[0] == 0xB0 && dd[1] == 0x04)
            {
                int32_t value;
                value = dd[5] + (dd[4] << 8) + (dd[3] << 16) + (dd[2] << 24);

                if (PLScanner::instance()->debugging)
                {
                    OiWarning() << "Task1: Read data:" << value;
                }

                d->_value1 = 1.0 * value;
                if (value / 1e3 > 10 && value / 1e3 <100)
                {
                    d->_temperature1 = d->_value1;
                }
                d->_waiter1.wake();
            }
        }

        void OptexSensorControl::onReadReady2(const QByteArray data)
        {
            OI_F(OptexSensorControl);

            uchar* dd = (uchar*)data.data();

            if (data.size() >= 2 && dd[0] == 0xC0 && dd[1] == 0x00)
            {
                if (PLScanner::instance()->debugging)
                {
                    OiWarning() << "Task2: Get Command return";
                }

                d->_waiter2.wake();
            }
            else if (data.size() >= 6 && dd[0] == 0xB0 && dd[1] == 0x04)
            {
                int32_t value;
                value = dd[5] + (dd[4] << 8) + (dd[3] << 16) + (dd[2] << 24);

                if (PLScanner::instance()->debugging)
                {
                    OiWarning() << "Task2: Read data:" << value;
                }

                d->_value2 = 1.0 * value;
                if (value / 1e3 > 0 && value / 1e3 < 100)
                {
                    d->_temperature2 = d->_value2;
                }
                d->_waiter2.wake();
            }
        }

        int OptexSensorControl::getStatus()
        {
            OI_F(OptexSensorControl);

            int ret = 0;

            if (d->_task1.connected() && d->_task2.connected())
            {
                ret |= DeviceStatus::DS_Linked;
            }
            else
            {
                ret &=  (~DeviceStatus::DS_Linked);
            }

            if (d->_saveStatus)
            {
                ret |= DeviceStatus::DS_Capture;
            }
            else
            {
                ret &= (~DeviceStatus::DS_Capture);
            }

            return ret;
        }

    }
}